package com.xunlai.infra.permission.common.util;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yanguoliang
 * @date 2021/4/25 21:32
 */
@Slf4j
public class SM2Utils {

    public static Signature signature;

//    public static KeyPair keyPair;

    public static final BouncyCastleProvider provider = new BouncyCastleProvider();

    public static Map<String,String> createKeyPair() {
        // 获取SM2 椭圆曲线推荐参数
        X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造EC 算法参数
        ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
                // 设置SM2 算法的 OID
                GMObjectIdentifiers.sm2p256v1.toString()
                // 设置曲线方程
                , ecParameters.getCurve()
                // 椭圆曲线G点
                , ecParameters.getG()
                // 大整数N
                , ecParameters.getN());
        try {
            // 创建 密钥对生成器
            KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            // 使用SM2的算法区域初始化密钥生成器
            gen.initialize(sm2Spec, new SecureRandom());
            // 获取密钥对
            KeyPair keyPair = gen.generateKeyPair();
            byte[] encoded = keyPair.getPublic().getEncoded();
            String publicKey = ByteUtils.toHexString(encoded);
            String privateKey = ByteUtils.toHexString(keyPair.getPrivate().getEncoded());
            Map<String,String> result = new HashMap<>();
            result.put("public",publicKey);
            result.put("private",privateKey);
            return result;
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return null;
        }
    }

    static {
        try {
            signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(),e);
        }
    }

    /**
     * 签名
     *
     * @param privateKey 签名私钥
     * @param plainText  明文
     * @return
     */
    public static String encrypt(PrivateKey privateKey, String plainText) {

        try {
            signature.initSign(privateKey);
            signature.update(plainText.getBytes());
            byte[] bytes = signature.sign();
            return ByteUtils.toHexString(bytes);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return null;
    }


    /**
     * 验证签名
     *
     * @param publicKey  签名公钥
     * @param signResult 签名结果
     * @param plainText  明文
     * @return
     */
    public static boolean verify(PublicKey publicKey, byte[] signResult, String plainText) {
        boolean result = false;
        try {
            signature.initVerify(publicKey);
            signature.update(plainText.getBytes());
            result = signature.verify(signResult);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return result;
    }

    /**
     * 验证签名
     *
     * @param publicKey  签名公钥
     * @param signResult 签名结果
     * @param plainText  明文
     * @return
     */
    public static boolean verify(String publicKey, String signResult, String plainText) {
        return verify(loadPublicKey(publicKey),ByteUtils.fromHexString(signResult),plainText);
    }
    /**
     * 验证签名
     *
     * @param publicKey  签名公钥
     * @param signResult 签名结果
     * @param plainText  明文
     * @return
     */
    public static boolean verify(PublicKey publicKey, String signResult, String plainText) {
        return verify(publicKey,ByteUtils.fromHexString(signResult),plainText);
    }

    public static PrivateKey loadPrivateKey(String privateKeyStr){
        byte[] bytes = ByteUtils.fromHexString(privateKeyStr);
        // 根据采用的编码结构反序列化公私钥
        try {
            KeyFactory keyFact = KeyFactory.getInstance("EC", provider);
            return keyFact.generatePrivate(new PKCS8EncodedKeySpec(bytes));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
            log.error("加载公钥失败："+e.getMessage(),e);
        }
        return null;
    }

    /**
     *
     * @param publicKeyStr HexString
     * @return
     */
    public static PublicKey loadPublicKey(String publicKeyStr){

        KeyFactory keyFact = null;
        try {
            byte[] bytes = ByteUtils.fromHexString(publicKeyStr);
            keyFact = KeyFactory.getInstance("EC", provider);
            return keyFact.generatePublic(new X509EncodedKeySpec(bytes));
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            log.error("加载私钥失败："+e.getMessage(),e);
            // 根据采用的编码结构反序列化公私钥
            return null;
        }
    }
}
